package helper

import (
	"fmt"
	"math"
	"math/rand"
	"reflect"
	"regexp"
	"sort"
	"strconv"
	"strings"
	"time"
)

// HttpQueryBuilder 组装签名字符串
func HttpQueryBuilder(p map[string]interface{}) string {
	var b strings.Builder
	for k, v := range p {
		b.WriteString(k)
		b.WriteString("=")
		b.WriteString(InterfaceToString(v))
		b.WriteString("&")
	}
	return strings.Trim(b.String(), "&")
}

// SignStrBuilder 组装签名字符串
func SignStrBuilder(p []string, v map[string]interface{}) string {
	var b strings.Builder
	for _, k := range p {
		b.WriteString(k)
		b.WriteString("=")
		b.WriteString(InterfaceToString(v[k]))
		b.WriteString("&")
	}
	return strings.Trim(b.String(), "&")
}

// InterfaceToString 空接口类型断言转字符串
func InterfaceToString(i interface{}) string {
	switch r := i.(type) {
	case string:
		return r
	case int64:
		return strconv.FormatInt(r, 10)
	case int:
		return strconv.Itoa(r)
	case float64:
		return strconv.FormatFloat(r, 'f', -1, 64)
	case []byte:
		return string(r)
	default:
		if i != nil {
			return fmt.Sprint(r)
		}
	}
	return ""
}

// InterfaceToInt 空接口类型断言转整型
func InterfaceToInt(i interface{}) (s int) {
	switch i.(type) {
	case string:
		s, _ = strconv.Atoi(i.(string))
	case int64:
		s = int(i.(int64))
	case int:
		s = i.(int)
	case float64:
		s = int(i.(float64))
	case float32:
		s = int(i.(float32))
	default:
		s = 0
	}
	return
}

// InterfaceToFloat64 空接口类型断言转浮点型
func InterfaceToFloat64(i interface{}) (s float64) {
	switch i.(type) {
	case string:
		s, _ = strconv.ParseFloat(i.(string), 64)
	case int64:
		s = float64(i.(int64))
	case int:
		s = float64(i.(int))
	case float64:
		s = i.(float64)
	case float32:
		s = float64(i.(float32))
	}
	return
}

// CheckSign 签名校验
func CheckSign(params map[string]interface{}, sign string, key string) bool {
	var signFields []string
	for k := range params {
		signFields = append(signFields, k)
	}

	sort.Strings(signFields)

	// 加入密钥
	signFields = append(signFields, "key")
	params["key"] = key

	signStr := SignStrBuilder(signFields, params)
	if Md5(signStr) == sign {
		return true
	}
	return false
}

// CreateMd5Sign 创建签名
func CreateMd5Sign(params map[string]interface{}, key string) string {
	var signFields []string
	for k := range params {
		signFields = append(signFields, k)
	}

	sort.Strings(signFields)

	// 加入密钥
	signFields = append(signFields, "key")
	params["key"] = key

	signStr := SignStrBuilder(signFields, params)

	return Md5(signStr)
}

// CheckParam 校验参数
func CheckParam(param interface{}, checkType string) (res bool, err string) {
	res = false
	err = "PARAMS_ERROR"
	switch checkType {
	case "mobile":
		if v, ok := param.(string); ok {
			if len(v) == 0 {
				err = "PARAMS_EMPTY"
				return
			}
			r, _ := regexp.Compile(`^1[3-9]\d{9}$`)
			res = r.MatchString(v)
		}
	case "email":
		if v, ok := param.(string); ok {
			if len(v) == 0 {
				err = "PARAMS_EMPTY"
				return
			}
			r, _ := regexp.Compile(`^[_a-z0-9-]+(\.[_a-z0-9-]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,})$`)
			res = r.MatchString(v)
		}
	case "orderid":
		if v, ok := param.(string); ok {
			if len(v) == 0 {
				err = "PARAMS_EMPTY"
				return
			}
			r, _ := regexp.Compile(`^\w{4,32}$`)
			res = r.MatchString(v)
		}
	case "md5":
		if v, ok := param.(string); ok {
			if len(v) == 0 {
				err = "PARAMS_EMPTY"
				return
			}
			r, _ := regexp.Compile(`^[a-fA-F0-9]{32}$`)
			res = r.MatchString(v)
		}
	case "timestamp":
		if v, ok := param.(int); ok {
			if v == 0 {
				err = "PARAMS_EMPTY"
				return
			}
			if v < 1554719604 {
				return
			}
			if math.Abs(float64(v)-float64(time.Now().Unix())) > 600 {
				err = "TIME_ERROR"
				return
			}
			res = true
		}
	}
	return
}

// CheckParamByRegex 自定义正则参数校验
func CheckParamByRegex(param string, pattern string) bool {
	res := false
	if r, err := regexp.Compile(pattern); err == nil {
		res = r.MatchString(param)
	}
	return res
}

// CreateRandStr 随机字符串
func CreateRandStr(size int, kind int) []byte {
	// KC_RAND_KIND_NUM   = 0 // 纯数字
	// KC_RAND_KIND_LOWER = 1 // 小写字母
	// KC_RAND_KIND_UPPER = 2 // 大写字母
	// KC_RAND_KIND_ALL   = 3 // 数字、大小写字母
	ikind, kinds, result := kind, [][]int{{10, 48}, {26, 97}, {26, 65}}, make([]byte, size)
	isAll := kind > 2 || kind < 0
	rand.Seed(time.Now().UnixNano())
	for i := 0; i < size; i++ {
		if isAll { // random ikind
			ikind = rand.Intn(3)
		}
		scope, base := kinds[ikind][0], kinds[ikind][1]
		result[i] = uint8(base + rand.Intn(scope))
	}
	return result
}

// Struct2Map 结构体转 map
func Struct2Map(obj interface{}) map[string]interface{} {
	t := reflect.TypeOf(obj)
	v := reflect.ValueOf(obj)

	var data = make(map[string]interface{})
	for i := 0; i < t.NumField(); i++ {
		data[t.Field(i).Name] = v.Field(i).Interface()
	}
	return data
}

// ToMap 结构体转为Map[string]interface{}
func ToMap(in interface{}, tagName string) (map[string]interface{}, error) {
	out := make(map[string]interface{})

	v := reflect.ValueOf(in)
	if v.Kind() == reflect.Ptr {
		v = v.Elem()
	}

	if v.Kind() != reflect.Struct { // 非结构体返回错误提示
		return nil, fmt.Errorf("ToMap only accepts struct or struct pointer; got %T", v)
	}

	t := v.Type()
	// 遍历结构体字段
	// 指定tagName值为map中key;字段值为map中value
	for i := 0; i < v.NumField(); i++ {
		fi := t.Field(i)
		key := fi.Name
		if tagName != "" {
			if tagValue := fi.Tag.Get(tagName); tagValue != "" {
				key = tagValue
			}
		}
		out[key] = v.Field(i).Interface()
	}
	return out, nil
}

// PointerStruct2Map *结构体转 map
func PointerStruct2Map(obj interface{}) map[string]interface{} {
	objv := reflect.ValueOf(obj)
	v := objv.Elem()
	typeOfType := v.Type()
	var data = make(map[string]interface{})
	for i := 0; i < v.NumField(); i++ {
		field := v.Field(i)
		data[typeOfType.Field(i).Name] = field.Interface()
	}
	return data
}
